home *** CD-ROM | disk | FTP | other *** search
- ANSWERS TO FREQUENTLY ASKED PASCAL QUESTIONS
- ============================================
-
- 1...
-
- Q. How do I pass an error level code when my program finishes?
-
- A. The halt procedure takes an optional parameter of type word. Thus -
-
- halt(1);
-
- terminates the program with an errorlevel of 1. If halt is used without
- a parameter it is the same as -
-
- halt(0);
-
- Note: When a program is terminated using the halt procedure any exit
- procedure that has previously been set up is executed.
-
-
- 2...
-
- Q. How do I empty the keyboard buffer?
-
- A. There are several ways that this can be achieved. However the safest
- is -
-
- while Keypressed do ch := ReadKey;
-
- This requires that a variable ch of type char is declared and the crt
- unit be used. To do it without using a variable -
-
- while Keypressed do while ReadKey = #0 do;
-
- or if using TP6 with extended syntax enabled -
-
- while KeyPressed do ReadKey;
-
- If you do not wish to incur the substantial overhead involved with the
- use of the CRT unit and there is no requirement for the program to run
- under a multi-tasker -
-
- var
- head : byte absolute $40:$1c;
- tail : byte absolute $40:$1e;
-
- tail := head;
-
- 3...
-
- Q. When I redirect the screen output of my programs to a file the file is
- empty and the output still appears on the screen. What am I doing
- wrong?
-
- A. You are probably using the CRT unit and its default method of writing
- to stdout is by direct screen writes. In order to enable output to be
- redirected all writes must be done by DOS. Setting the variable
- DirectVideo to false has no effect on redirection as all it does is use
- the BIOS for screen writes - not DOS.
-
- To enable redirection you must not use the CRT unit
-
- OR
-
- assign(output,'');
- rewrite(output);
-
- This will make all output go through DOS and thus can be redirected if
- desired. To restore the default situation -
-
- AssignCRT(output); rewrite(output);
-
-
- 4...
-
- Q. How do I make a string that is lower or mixed case all uppercase?
-
- A. There are several ways to convert lower case characters to upper case.
- Here are some of them.
-
- As a procedure (excluding asm code this is the fastest way)
-
- procedure StrUpper(var st: string);
- var x : byte;
- begin
- for x := 1 to length(st) do
- st[x] := UpCase(st[x]);
- end;
-
- As a function (slower but sometimes more convenient) -
-
- function StrUpper(st: string): string;
- var x : byte;
- begin
- StrUpper[0] := st[0];
- for x := 1 to length(st) do
- StrUpper[x] := UpCase(st[x]);
- end;
-
- Both the above are suitable for the English language . However from
- version 4.0 onwards, DOS has had the facility to do this in a way that
- is country (language) specific. I am indebted to Norbert Igl for the
- basic routine. I have modified his code slightly. For the anti-goto
- purists this is a good example of a goto that is convenient, efficient,
- self-documenting and structured. The dos calls would make this method
- the slowest of all.
-
- function StrUpper(s: string): string;
- { Country specific string-to-uppercase conversion. Requires DOS unit }
- label
- fail;
- var
- regs : registers;
- x : byte;
- begin
- if lo(DosVersion) >= 4 then begin
- with regs do begin
- ax := $6521;
- ds := seg(s);
- dx := ofs(s[1]);
- cx := length(s);
- msdos(regs);
- if odd(flags) then { the attempted conversion failed so }
- goto fail;
- end; { with }
- end { if DOS >= 4.0 } else
- fail:
- for x := 1 to length(s) do
- s[x] := UpCase(s[x]);
- StrUpper := s;
- end; { StrUpper }
-
-
-
- 5...
-
- Q. When I include ANSI codes in a string and write that string to the
- screen the actual codes appear on the screen, rather than the results
- they are supposed to achieve.
-
- A. In order for ANSI codes to be interpreted, screen writes must be
- directed through DOS and there must have been a suitable driver loaded
- via the config.sys file at boot time. All output can be directed
- through DOS and the driver by -
-
- Not using the crt unit
-
- OR -
-
- assign(output,'');
- rewrite(output);
-
- in which case ALL screen writes are "ANSI code sensitive"
-
- OR -
-
- You can set up write procedures that will be "ANSI code sensitive".
- (You will need an initialisation procedure to set this up.)
-
- var
- ansi : text;
-
- procedure AssignANSI(var ansifile : text);
- begin
- assign(ansifile,'CON');
- rewrite(ansifile);
- end; { AssignANSI }
-
- procedure WriteANSI(var st: string);
- begin
- write(ansi,st)
- end; { WriteANSI }
-
- procedure WriteLnANSI(var st: string);
- begin
- writeANSI(st);
- writeln(ansi);
- end; { WriteANSI }
-
- ObviousLy, if the ANSI.SYS driver (or an equivalent) is not installed
- none of the above can work.
-
- Setting the variable DirectVideo in the CRT unit to false will not
- achieve the desired result as this merely turns off direct screen
- writes and uses the BIOS for all screen output.
-
-
- 6...
-
- Q. When I try to shell to DOS nothing happens. What am I doing wrong?
-
- A. In order to be able to execute any child process there must be
- sufficient memory available for it to load and execute. Unless you
- advise differently at compile time, a Turbo Pascal program grabs all
- available memory for itself when it is loaded. To reserve memory for a
- child process use the compiler memory directive -
-
- {$M 16384,0,0)
- the default is -
- {$M 16384,0,655360}
-
- The first figure - StackMin - is the amount of memory to be allocated
- for the stack:
-
- Minimum is: 1024
- Default is: 16384
- Maximum is: 65520
-
- The next figure - HeapMin -is the minumum amount of memory to be
- allocated for the heap. If there is less memory available than this
- figure the program will not load.
-
- Minimum is: 0
- Default is: 0
- Maximum is: 655360 In practice it will be the amount of free
- memory less the space required for the stack,
- less the code space of the program. You should
- set this to 0 unless your program uses the
- heap. In that case, set it to the lowest
- possible figure to prevent heap allocation
- errors. In most cases it is best to leave it
- at zero and do error checking within the
- program for sufficient memory at allocation
- time.
-
- The last figure is the crucial on as regards child processes. It
- should always be low enough to leave memory left over for a child
- process and high enough not to cause problems for the program when
- allocating heap memory.
-
- Minimum is: HeapMin
- Default is: 655360
- Maximum is: 655360 If less than the requested amount is available
- no error is reorted. Instead all available
- memory is allocated for heap use.
-
-
-
- 7...
-
- Q. How do I shell to DOS?
-
- A. SwapVectors;
- exec(GetEnv('COMSPEC','');
- SwapVectors;
-
- Read previous section on memory allocation.
-
- I find that it is a good idea to write my own Exec function which will
- do everything that is needed for me. I have it return an integer value
- that is the DosError code.
-
- function Exec(p1,p2: string);
- begin
- SwapVectors;
- Dos.Exec(p1,p2);
- SwapVectors;
- Exec := DosError;
- end;
-
- This enables me to have a statement such as -
-
- ReportError(Exec(GetEnv('COMPSEC'),''));
-
- Now you can have an empty ReportError procedure or you can make it
- report the error - whatever is suitable for you application.
-
-
- 8...
-
- Q. When I execute a child process redirection does not work. Why?
-
- A. Redirection of a child process's output only works if it is run under
- another copy of the command processor. So -
-
- exec('YourProg.exe',' > nul'); will not work but
- exec(GetEnv('COMSPEC'),'/c YourProg > nul'); will work.
-
-
- 9...
-
- Q. How do I read an errorlevel from a child process?
-
- A. After executing a child process the errorlevel returned can be read
- by calling the DosExitCode function which returns a word. The low
- byte is the errorlevel. A full description is in the manual.
-
- If the command interpreter is the child process and it in turn
- executes a child process then the errorlevel of the second child
- process cannot be read without resorting to some trickery.
-
-
- 10...
-
- Q. When I read a text file that has lines exceeding 255 characters I
- lose all those characters from the 256th one on each time there is a
- line that exceeds that length. How can I prevent this?
-
- A. Turbo Pascal's readln procedure reads a line up to the 255th
- character then skips to the next line. To get around this you
- should declare a buffer at least as large as the longest possible
- line and then use the read procedure. The best size for the buffer
- is a multiple of 2048 bytes.
-
- const
- BufferSize = 2048;
- LineLength = 78;
- type
- textbuffer = array[1..BufferSize] of char;
- var
- st : string;
- f : text;
- buffer : textbuffer;
-
- function ReadTxtLn(var tf: text; var s: string; max: byte): integer;
- { Reads a string of a maximum length from a text file }
- var
- len : byte absolute s;
- begin
- len := 0;
- {$I-}
- while (len < max) and not eoln(tf) do begin
- inc(len);
- read(tf);
- end;
- if eoln(tf) then
- readln(tf);
- ReadTxtLn := IOResult;
- {$I+}
- end; { ReadTxtLn }
-
- begin
- assign(f,filename);
- reset(f);
- SetTextBuf(f,buffer);
- while not eof(f) and (ReadTxtLn(f,st,LineLength) = 0) do
- writeln(st);
- close(f);
- end.
-
-
- 11...
-
- Q. How do I convert nul terminated asciiz strings to Turbo Pascal
- strings?
-
- A. Here is a function that will do that -
-
- function Asc2Str(var s; max: byte): string;
- { Converts an ASCIIZ string to a Turbo Pascal string }
- { with a maximum length of max. }
- var starray : array[1..255] of char absolute s;
- len : integer;
- begin
- len := pos(#0,starray)-1; { Get the length }
- if (len > max) or (len < 0) then { length exceeds maximum }
- len := max; { so set to maximum }
- Asc2Str := starray;
- Asc2Str[0] := chr(len); { Set length }
- end; { Asc2Str }
-
-
- 12...
-
- Q. How can I tell if a particular bit of a variable is set or not? How can
- I set it? How can I turn it off? How can I make a large bit map and
- then determine if a particular bit - say bit 10000 is on/of?
-
- A. This question, or a variation of it, is one of the most commonly asked
- questions in the echo and there are several ways of doing what is
- wanted. None are necessarily right or wrong. The way I will describe
- is designed to take up as little code/data space as possible. I do not
- attempt to explain the theory behind these functions as this can be
- obtained from any good book. Question 16 also contains valuable extra
- help on the subject of truth tables.
-
- The use of sets can be the best bit manipulation method if you have
- control over the data being used. Here is an example of a byte variable
- for a BBS program which sets various user access level flags.
-
- Bit 0 = Registered User
- 1 = Twit
- 2 = Normal
- 3 = Extra
- 4 = Privileged
- 5 = Visiting Sysop
- 6 = Assistant Sysop
- 7 = Sysop
-
- type
- status_type = (Registered,
- Twit,
- Normal,
- Extra,
- Privileged,
- VisitingSysop,
- AssistantSysop,
- Sysop);
- status_level = set of status_type;
-
- var
- access_flags : status_level;
-
- Let us assume you have someone who logs on and you wish to determine
- his user access level. After reading access_flags from the user data
- file -
-
- if Sysop in access_flags then ....
-
- To set the sysop flag -
-
- access_flags := access_flags + [Sysop];
-
- To reset the sysop flag -
-
- access_flags := access_flags - [Sysop];
-
- However on many occasions using a set may not be a suitable method.
- You may simply need to know if bit 5 is set or not. Here is the method
- that I consider the best -
-
- function BitIsSet(var V, bit: byte): boolean;
- begin
- BitIsSet := odd(V shr bit);
- end;
-
- To set a bit -
-
- procedure SetBit(var V: byte; bit: byte);
- begin
- V := V or (1 shl bit);
- end;
-
- To reset a bit -
-
- procedure ResetBit(var V: byte; bit: byte);
- begin
- V := V and not(1 shl bit);
- end;
-
- To toggle (flip) a bit -
-
- procedure ToggleBit(var V: byte; bit: byte);
- begin
- V := V xor (1 shl bit);
- end;
-
- Now a bit map can be made up from an array of bytes. If stored on the
- heap you can test any bit up to number 524159 (zero based). Here's
- how.
-
- type
- map = array[0..maxsize] of byte;
- { set maxsize to number of bits div 8 -1 needed in the bit map }
-
- function BitSetInBitMap(var x; numb : longint): boolean;
- { Tests the numb bit in the bitmap array }
- var m: map absolute x;
- begin
- BitSetInBitMap := odd(m[numb shr 3] shr (numb and 7));
- end;
-
- procedure SetBitInBitMap(var x; numb: word);
- { Sets the numb bit in the bitmap array }
- var m: map absolute x;
- begin
- m[numb shr 3] := m[numb shr 3] or (1 shl (numb and 7))
- end;
-
- procedure ResetBitInBitMap(var x; numb : longint);
- { Resets the numb bit in the bitmap array }
- var m: map absolute x;
- begin
- m[numb shr 3] := m[numb shr 3] and not(1 shl (numb and 7));
- end;
-
- procedure ToggleBitInBitMap(var x; numb : longint);
- { Toggles (flips) the numb bit in the bitmap array }
- var m: map absolute x;
- begin
- m[numb shr 3] := m[numb shr 3] xor (1 shl (numb and 7));
- end;
-
-
- 13...
-
- Q. How can I find a particular string in any file - text or binary?
-
- A. The Boyer-Moore string search algorithm is considered to be the fastest
- method available. However in a rare worst-case scenario it can be
- slightly slower than a linear brute-force method. The following
- demonstration program will show how it works and could easily be
- modified to allow for command line paramters etc.
-
-
- program BMSearchDemo;
-
- type
- bigarray = array[0..32767] of byte;
- baptr = ^bigarray;
- BMTable = array[0..255] of byte;
-
- const
- KeyStr : string = 'Put whatever you want found here';
- fname : string = 'f:\Filename.txt';
-
- var
- Btable : BMtable;
- buffer : baptr;
- f : file;
- result,
- position : word;
- offset : longint;
- finished,
- Strfound : boolean;
-
- procedure MakeBMTable(var t : BMtable; var s);
- { Makes a Boyer-Moore search table. s = the search string t = the table }
- var
- st : BMtable absolute s;
- slen: byte absolute s;
- x : byte;
- begin
- FillChar(t,sizeof(t),slen);
- for x := slen downto 1 do
- if (t[st[x]] = slen) then
- t[st[x]] := slen - x
- end;
-
- function BMSearch(var buff,st; size : word): word;
- { Not quite a standard Boyer-Moore algorithm search routine }
- { To use: pass buff as a dereferenced pointer to the buffer}
- { st is the string being searched for }
- { size is the size of the buffer }
- { If st is not found, returns $ffff }
- var
- buffer : bigarray absolute buff;
- s : array[0..255] of byte absolute st;
- len : byte absolute st;
- s1 : string absolute st;
- s2 : string;
- count,
- x : word;
- found : boolean;
- begin
- s2[0] := chr(len); { sets the length to that of the search string }
- found := false;
- count := pred(len);
- while (not found) and (count < (size - len)) do begin
- if (buffer[count] = s[len]) then { there is a partial match } begin
- if buffer[count-pred(len)] = s[1] then { less partial! } begin
- move(buffer[count-pred(len)],s2[1],len);
- found := s1 = s2; { if = it is a complete match }
- BMSearch := count - pred(len); { will stick unless not found }
- end;
- inc(count); { bump by one char - match is irrelevant }
- end
- else
- inc(count,Btable[buffer[count]]); { no match so increment maximum }
- end;
- if not found then
- BMSearch := $ffff;
- end; { BMSearch }
-
-
- begin
- new(buffer);
- assign(f,fname);
- reset(f,1);
- offset := 0;
- MakeBMTable(Btable,KeyStr);
- repeat
- BlockRead(f,buffer^,sizeof(buffer^),result);
- position := BMSearch(buffer^,KeyStr,result);
- finished := (result < sizeof(buffer^)) or (position <> $ffff);
- if position = $ffff then
- inc(offset,result);
- Strfound := position <> $ffff;
- until finished;
- close(f);
- if Strfound then
- writeln('Found at offset ',offset)
- else
- writeln('Not found');
- end.
-
- 14...
-
- Q. How can I put a apostrophe in a string?
-
- A. Just put in extra apostrophes. If you want st to be equal to the
- string -
- The word 'quoted' is in quotes
- do this -
- st := 'The word ''quoted'' is in quotes';
-
- if you want the following to be written to screen -
- 'This is a quoted string'
- do this -
- writeln('''This is a quoted string''');
-
-
- 15...
-
- Q. What are the best books to purchase to help me learn Turbo Pascal?
-
- A. There are many good books for learners. Here are a few -
-
- Complete Turbo Pascal - Third Edition - Jeff Duntemann
- Mastering Turbo Pascal 6 - Tom Swann
- Turbo Pascal - The Complete Reference - O'Brien.
-
- For advanced users there are also many good books. Here are a few
- that I have found useful - (Those marked with an asterisk are not
- purely for Turbo Pascal)
-
- Turbo Pascal 6 - Techniques and Utilities - Rubenking
- Turbo Pascal Internals - Tischer
- * PC System Programming for Developers - Tischer
- * Undocumented DOS - Schulman
-
- Any learner would be well advised to obtain a well known library
- such as Technojock's Turbo Toolkit (TTT) which is shareware and
- study the source code.
-
- 16.
-
- Q. hat are "truth tables" and how do they work?
-
- A. Truth tables are a set of rules that are used to determine the result of
- logical operations. The logical operators are -
-
- NOT
- AND
- OR
- XOR.
-
- Here is a brief explanation of truth tables. When two values are
- logically compared by using a logical operator each bit of one value is
- directly compared to the corresponding bit in the other value and the
- same bit in the returned value is set or reset according to the
- following truth table.
-
- NOT AND OR XOR
- not 1 = 0 0 and 0 = 0 0 or 0 = 0 0 xor 0 = 0
- not 0 = 1 0 and 1 = 0 0 or 1 = 1 0 xor 1 = 1
- 1 and 0 = 0 1 or 0 = 1 1 xor 0 = 1
- 1 and 1 = 1 1 or 1 = 1 1 xor 1 = 0
-
- NOT reverses the bit.
- AND sets the returned bit if both compared bits are set.
- OR sets the returned bit if either of the compared bits are set.
- XOR sets the returned bit if the compared bits are not the same.
-
-
- 17.
-
- Q. What are pointers and how can I use them? I have heard that they are
- variables that can be created and discarded as required thus saving
- memory. Is this true?
-
- A. A pointer is a variable that contains a memory address.
-
- The heap is all of that memory allocated by DOS to a program for its
- use that has not been used by the program for its code, global data or
- stack.
-
- Dynamic variables are variables that have had space allocated for them
- on the heap.
-
- Dynamic variables have no identifier (are unnamed). Because of this
- they need an associated variable that can be used to find where they
- reside in memory. Pointers are ideal for this but need some method to
- define what type of data it is that they are pointing at. Pascal
- provides this method.
-
- type
- Str10Ptr = ^string[10];
- { This means Str10Ptr is a pointer that points to data of type }
- { string[10]. }
- var
- S : Str10Ptr;
-
- In the above example S is a pointer that has been defined as pointing
- to an address in memory that will contain (or should contain) data of
- type string[10].
-
- However how does S get this value? How does it know where that data's
- address is supposed to be? Well until the programmer allocates memory
- for that data S's value is undefined, so it could be literally
- pointing anywhere. So it is *vital* that before we try to use it to
- use/assign data from/to that memory location we give S a memory
- address that is not being used for any other purpose at the moment and
- that is big enough to hold the data that we want to place into it - in
- this case at least 11 bytes. We do this by -
-
- new(S);
-
- Pascal has now allocated at least 11 bytes of heap and has allocated S
- with the address of the FIRST byte of that allocation.
-
- Ok... so far so good! How do we access that data (remembering that it
- has no name). Well we "dereference" the pointer. This is done by
- placing a carat sign immediately following the pointer's identifier.
-
- S^ := 'Joe Bloggs';
-
- This statement actually means "Place the string 'Joe Bloggs' into the
- memory address that S contains". This is referred to as "derferencing"
- the pointer S.
-
- To "reference" a dynamic variable we "dereference" its associated
- pointer variable. We cannot say -
-
- S := 'Joe Bloggs';
-
- because S is a pointer and that would be trying to give a pointer a
- string type value - a compiler "type mismatch" would occur. So every
- time we wish to access that dynamic variable we dereference it.
-
- To delete the dynamic variable once it is of no further use is just a
- matter of -
-
- dispose(S);
-
- What this statement does is release the memory previously used by S^
- and make it available to be used for other purposes by the program.
- Depending on the version of Pascal you are using it may not erase or
- alter the contents of that memory and it may not give S a new value.
- However any attempt to dereference S is an error as the integrity of
- that memory location has been lost - it may have been allocated to
- other data.
-
- Pointers do not *have* to point to a memory location in the heap or
- even have their value always allocated by using the New procedure. Any
- valid memory address can be assigned to them and then they can be
- dereferenced as shown above. As a simple example of this lets say you
- want to examine the contents of the 16 byte area at $40:$f0 (the ICA
- area). You could - (TP specific)
-
- type
- ICA_Ptr = ^array[0..15] of byte;
- var
- B : byte;
- ICA : ICA_Ptr;
-
- ICA := ptr($40,$f0);
-
- Now ICA points to the address specified and you can dereference it -
-
- B := ICA^[10];
-
- Hope that helps get you started into the complex world of memory
- management and manipulation using pointers. There are countless
- permutations and methods that can be used.
-
-
- 18.
-
- Q. How do I do word wrap?
-
- A. The demo program WRAP.PAS in this archive demonstrates both word wrap
- and the justifying of text.
-
-
-
-
-
-
-
-
-
-